using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Mite.Configuration;
using Mite.DSL;
using Rhino.DSL;

namespace Mite
{
	/// <summary>
	/// Ties migrations together, running them in appropriate order.
	/// </summary>
	/// <remarks>
	/// Proposed replacement for the hard to test static Migrator.
	/// </remarks>
	public class MigrationRunner
	{
		private DslFactory _dslFactory;
		private IAdapterFactory _adapterFactory;

		public MigrationRunner(IAdapterFactory adapterFactory)
		{
			_dslFactory = new DslFactory();
			_dslFactory.Register<MigrationBase>(new MigrationDslEngine());
			_dslFactory.BaseDirectory = Environment.CurrentDirectory;
			_adapterFactory = adapterFactory;
		}

		public void MigrateUp(MiteEnvironment environment, string pathToMigrations)
		{
			var migrations = GetMigrationsFromDirectory(environment, pathToMigrations);
			Migrate(environment, migrations.Max(x => x.Version), migrations.ToArray());
		}

		public void Migrate(MiteEnvironment environment, uint targetVersion, params MigrationBase[] migrations)
		{
			var adapter = GetAdapter(environment);

			using (IDbConnection conn = adapter.CreateConnection(environment.Host, environment.Database, environment.Username, environment.Password, environment.Options))
			{
				var db = adapter.GetDatabase(conn, environment.Database);
				int version = adapter.GetSchemaVersion(conn, db);

				if (targetVersion == version)
					return;

				IEnumerable<MigrationBase> migrationsToRun;
				MigrationDirection direction;
				if (targetVersion < version)
				{
					direction = MigrationDirection.Down;
					migrationsToRun =
						migrations.Where(x => x.Version <= version && x.Version > targetVersion).OrderByDescending(x => x.Version);
				}
				else
				{
					direction = MigrationDirection.Up;
					migrationsToRun =
						migrations.Where(x => x.Version <= targetVersion && x.Version > version).OrderBy(x => x.Version);
				}

				foreach (var migration in migrationsToRun)
					migration.Run(direction);
			}
		}

		private IAdapter GetAdapter(MiteEnvironment environment)
		{
			return _adapterFactory.Create(environment);
		}

		private IEnumerable<MigrationBase> GetMigrationsFromDirectory(MiteEnvironment environment, string directory)
		{
			string[] migrationFiles = Directory.GetFiles(directory, "*.boo",
														 SearchOption.TopDirectoryOnly);
			var migrations = new List<MigrationBase>();

			foreach (string file in migrationFiles)
			{
				var migration = _dslFactory.TryCreate<MigrationBase>(file, environment);
				migrations.Add(migration);
			}
			return migrations.OrderBy(x => x.Version);
		}
	}
}